/****

activequant - activestocks.eu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


contact  : contact@activestocks.eu
homepage : http://www.activestocks.eu

****/
package org.activequant.tradesystem.ib;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.activequant.broker.BrokerBase;
import org.activequant.container.services.ITimeStampService;
import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.account.BalanceBook;
import org.activequant.core.domainmodel.account.BrokerAccount;
import org.activequant.core.domainmodel.account.Order;
import org.activequant.core.domainmodel.account.OrderBook;
import org.activequant.core.domainmodel.account.OrderHistory;
import org.activequant.core.domainmodel.account.Portfolio;
import org.activequant.core.domainmodel.account.Position;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.events.OrderAcceptEvent;
import org.activequant.core.domainmodel.events.OrderCancelEvent;
import org.activequant.core.domainmodel.events.OrderCommissionEvent;
import org.activequant.core.domainmodel.events.OrderCompletionEvent;
import org.activequant.core.domainmodel.events.OrderEvent;
import org.activequant.core.domainmodel.events.OrderExecutionEvent;
import org.activequant.core.domainmodel.events.OrderSubmitEvent;
import org.activequant.core.domainmodel.events.OrderUpdateEvent;
import org.activequant.core.types.Currency;
import org.activequant.core.types.OrderSide;
import org.activequant.core.types.OrderType;
import org.activequant.dao.IBalanceBookDao;
import org.activequant.dao.IOrderBookDao;
import org.activequant.dao.IPortfolioDao;
import org.activequant.dao.IPositionDao;
import org.activequant.data.retrieval.ICandleSubscriptionSource;
import org.activequant.data.retrieval.IQuoteSubscriptionSource;
import org.activequant.data.retrieval.ISubscription;
import org.activequant.data.retrieval.ITradeIndicationSubscriptionSource;
import org.activequant.util.pattern.events.IEventListener;
import org.activequant.util.tools.UniqueDateGenerator;

import com.ib.algokit.ib.CommissionsCalculator;

/**
* Paper broker: gets subscription to the quotes, and executed incoming orders
* according to the quotes. Supports only MARKET, LIMIT, STOP and Trailing Stop orders.
* <p>
* Based on the original AQ PaperBroker code from 2005. 
* </p>
* A BrokerBase subclass. Defines the OrderTracker(OrderTrackerBase subclass) class. Holds the following associated variables:
* <ul>
* <li>quoteSubscriptionSource(IQuoteSubscriptionSource)</li>
* <li>candleSubscriptionSource(ICandleSubscriptionSource)</li>
* <li>tradeIndicationSubscriptionSource(ITradeIndicationSubscriptionSource)</li>
* <li>commissionPerExecution(double)</li>
* <li>timeStampGenerator(UniqueDateGenerator)</li>
* <li>orderId(AtomicInteger)</li>
* <li>currentTimeStamp(AtomicReference&lt;TimeStamp&gt;)</li>
* <li>commissionPerShare(double)</li>
* <li>orderMap(Map&lt;Integer,OrderTracker&gt;)</li>
* <li>brokerAccount(BrokerAccount)</li>
* <li>orderBookDao(IOrderBookDao)</li>
* <li>portfolioDao(IPortfolioDao)</li>
* <li>positionDao(IPositionDao)</li>
* <li>balanceBookDao(IBalanceBookDao)</li>
* <li></li>
* <li></li>
* </ul>
* Holds the following inherited variables:
* <ul>
* <li>logger(IAQLogger)</li>
* <li>originalOrderMap(Map&lt;String,OrderTrackerBase&gt;)</li>
* </ul>
* <b>History:</b><br>
*  - [13.12.2007] Created (Mike Kroutikov)<br>
*  - [26.09.2009] Adding OrderSubmit and OrderAccept events (Ulrich Staudinger)<br>
*  - [21.11.2009] Adding Quote Not set checks. (Ulrich Staudinger)<br>
*
*  @author Mike Kroutikov
*/
public class IBPaperBroker extends BrokerBase {
	
	private boolean useDatabase=false;
	/**
	 * private IQuoteSubscriptionSource quoteSubscriptionSource;
	 */
	private IQuoteSubscriptionSource quoteSubscriptionSource;
	/**
	 * private ICandleSubscriptionSource candleSubscriptionSource;
	 */
	private ICandleSubscriptionSource candleSubscriptionSource;
	/**
	 * private ITradeIndicationSubscriptionSource tradeIndicationSubscriptionSource;
	 */
	private ITradeIndicationSubscriptionSource tradeIndicationSubscriptionSource;
	/**
	 * private final UniqueDateGenerator timeStampGenerator = new UniqueDateGenerator();
	 */
	private final UniqueDateGenerator timeStampGenerator = new UniqueDateGenerator();
	/**
	 * private final AtomicInteger orderId = new AtomicInteger();
	 */
	private final AtomicInteger orderId = new AtomicInteger();
	/**
	 * private AtomicReference&lt;TimeStamp&gt; currentTimeStamp = new AtomicReference&lt;TimeStamp&gt;(new TimeStamp(0L));
	 */
//	private AtomicReference<TimeStamp> currentTimeStamp = new AtomicReference<TimeStamp>(new TimeStamp(0L));
	
	private final ITimeStampService timeStampService;
	/**
	 * private final Map&lt;Integer,OrderTracker&gt; orderMap = new ConcurrentHashMap&lt;Integer,OrderTracker&gt;();<br/>
	 * for decoding events
	 */
	private final Map<Integer,OrderTracker> orderMap = new ConcurrentHashMap<Integer,OrderTracker>();
	/**
	 * private final BrokerAccount brokerAccount;
	 */
	private final BrokerAccount brokerAccount;
	/**
	 * private final IOrderBookDao orderBookDao;
	 */
	private final IOrderBookDao orderBookDao;
	/**
	 * private final IPortfolioDao portfolioDao;
	 */
	private final IPortfolioDao portfolioDao;
	/**
	 * private final IPositionDao positionDao;
	 */
	private final IPositionDao positionDao;
	/**
	 * private final IBalanceBookDao balanceBookDao;
	 */
	private final IBalanceBookDao balanceBookDao;
	
	private final Map<InstrumentSpecification,ISubscription<Quote>> subscriptions= new HashMap<InstrumentSpecification,ISubscription<Quote>>();
	/**
	 * constructs a PaperBroker(BrokerBase subclass) using the given account(BrokerAccount), quoteSubscriptionSource(IQuoteSubscriptionSource), candleSubscriptionSource(ICandleSubscriptionSource) and tradeIndicationSubscriptionSource(ITradeIndicationSubscriptionSource) to set its associated 
	 * brokerAccount(BrokerAccount), quoteSubscriptionSource(IQuoteSubscriptionSource), candleSubscriptionSource(ICandleSubscriptionSource) and tradeIndicationSubscriptionSource(ITradeIndicationSubscriptionSource)<br/>
	 * Sets the value of its associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;) with the given start(TimeStamp)<br/>
	 * Sets its associated orderBookDao(IOrderBookDao), portfolioDao(IPortfolioDao), positionDao(IPositionDao) and balanceBookDao(IBalanceBookDao) with the given
	 * orderBookDao(IOrderBookDao), portfolioDao(IPortfolioDao), positionDao(IPositionDao) and balanceBookDao(IBalanceBookDao)
	 * @param subscriptionSource
	 */
	public IBPaperBroker(ITimeStampService timeStampService, IOrderBookDao orderBookDao, IPortfolioDao portfolioDao, IPositionDao positionDao, IBalanceBookDao balanceBookDao, BrokerAccount account, IQuoteSubscriptionSource quoteSubscriptionSource, ICandleSubscriptionSource candleSubscriptionSource, ITradeIndicationSubscriptionSource tradeIndicationSubscriptionSource) {
		this.orderBookDao = orderBookDao; 
		this.portfolioDao = portfolioDao; 
		this.positionDao = positionDao; 
		this.balanceBookDao = balanceBookDao; 
		this.brokerAccount = account; 
		this.quoteSubscriptionSource = quoteSubscriptionSource;
		this.candleSubscriptionSource = candleSubscriptionSource;
		this.tradeIndicationSubscriptionSource = tradeIndicationSubscriptionSource;
		this.timeStampService=timeStampService;
//		this.currentTimeStamp.set(start);
	}
	
	/**
	 * increments the associated orderId(AtomicInteger) by 1 and returns the updated value
	 * @return
	 */
	private int getNextOrderId() {
		return orderId.incrementAndGet();
	}
	/**
	 * returns a Date value of the current value in the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;)
	 * @return
	 */
	private Date currentDate() {
//		return currentTimeStamp.get().getDate();
		return timeStampService.currentTimeStamp().getDate();
	}	
	/**
	 * An OrderTrackerBase subclass. Holds the following associated variables:
	 * <ul>
	 * <li>orderId(int)</li>
	 * <li>localOrder(Order)</li>
	 * <li>subscription(ISubscription&lt;Quote&gt;)</li>
	 * <li>openQuantity(double)</li>
	 * <li>orderHistory(OrderHistory)</li>
	 * </ul>
	 * Holds the following inherited variables:
	 * <ul>
	 * <li>orderEvent(Event&lt;OrderEvent&gt;)</li>
	 * <li>order(Order)</li>
	 * <li>isSubmitted(AtomicBoolean)</li>
	 * <li>isCanceled(AtomicBoolean)</li>
	 * <li>brokerAssignedId(String)</li>
	 * <li>completion(AtomicReference&lt;OrderCompletionEvent&gt;)</li>
	 * <li>commission(AtomicReference&lt;OrderCommissionEvent&gt;)</li>
	 * <li>openQuantity(double)</li>
	 * <li>totalQuantity(double)</li>
	 * <li>executions(List&lt;OrderExecutionEvent&gt;)</li>
	 * <li>commissions(List&lt;OrderCommissionEvent&gt;)</li>
	 * </ul>
	 * @author Dimitar
	 *
	 */
	class OrderTracker extends OrderTrackerBase {
		/**
		 * private final int orderId;
		 */
		private final int orderId;
		/**
		 * private Order localOrder;<br/>
		 * will change for trailing stop strategy
		 * therefore, keep separate from the 
		 * one in base class...
		 */
		private Order localOrder; // will change for trailing stop strategy
								  // therefore, keep separate from the 
								  // one in base class...
		/**
		 * private ISubscription&lt;Quote&gt; subscription;
		 */
		private ISubscription<Quote> subscription;
		/**
		 * private final InstrumentSpecification spec;
		 */
		private final InstrumentSpecification spec;
		/**
		 * private double openQuantity;
		 */
		private double openQuantity;
		/**
		 * private final OrderHistory orderHistory;
		 */
		private final OrderHistory orderHistory;
		/**
		 * constructs an OrderTracker(OrderTrackerBase subclass) using the given order(Order) and its quantity(double) to set its inherited order(Order) and openQuantity(double).
		 * Sets its associated localOrder(Order) with a copy of the given order(Order).<br/>
		 * Sets its associated openQuantity(double) with the quantity(double) of the given order(Order).<br/>
		 * Sets its spec(InstrumentSpecification) with the instrumentSpecification(InstrumentSpecification) of the given order(Order).<br/>
		 * Sets its associated orderId(long) with the next order id generated by the inherited orderId(AtomicLong)
		 * @param order
		 */
		public OrderTracker(Order order) {
			super(order);
			localOrder = new Order(order);
			orderId = getNextOrderId();
			spec = order.getInstrumentSpecification();
			openQuantity = order.getQuantity();
			//order.setId(((long)orderId));
			orderMap.put(orderId, this);
			orderHistory = new OrderHistory();
			orderHistory.setOrder(order);
			final OrderBook orderBook=brokerAccount.getOrderBook();
			orderBook.addHistory(orderHistory);
			if(useDatabase){
				orderBookDao.update(orderBook);
			}
			getOrderEventSource().addEventListener(new IEventListener<OrderEvent>() {
				public void eventFired(OrderEvent event) {
					orderHistory.getEvents().add(event);
					
					if(event instanceof OrderCompletionEvent) {
						// no more need to manage: order is final
						orderHistory.setHistoryCompletion((OrderCompletionEvent) event);
						//managedOrders.remove(getBrokerAssignedId());
					} else if(event instanceof OrderExecutionEvent) {
						processExecution((OrderExecutionEvent) event);
					} else if(event instanceof OrderCommissionEvent) {
						processCommission((OrderCommissionEvent) event);
					}
					if(useDatabase){
						//getOrderEventSource().fire(event);
						orderBookDao.update(orderBook);
					}
				}
			});
			
		}
		/**
		 * <strong>1.<strong> Sets the inherited brokerAssignedId(String) with the associated orderId(String).<br/>
		 * <strong>2.<strong> Creates and fires an OrderSubmitEvent whose timeStamp is from the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;) and an "ok" message.<br/>
		 * <strong>3.<strong> Creates and fires an OrderAcceptEvent whose timeStamp is from the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;), orderId(String) and an "ok" message.<br/>
		 * <strong>4.<strong> Sets the associated subscription(ISubscription&lt;Quote&gt;) by subscribing to the associated subscriptionSource(IQuoteSubscriptionSource) for the instrumentSpecification(InstrumentSpecification)
		 * of the associated localOrder(Order).<br/>
		 * <strong>5.<strong> Registers a new IEventListener&lt;Quote&gt; to the associated subscription(ISubscription&lt;Quote&gt;) whose <code>eventFired(Quote event)</code> method
		 * updates the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;) and decides whether the associated localOrder(Order) can execute based on the received 
		 * event(Quote) and invokes <code>fireExecution(quantity, price)</code> if so.<br/>
		 * <strong>6.<strong> Activates the associated subscription(ISubscription&lt;Quote&gt;) and then returns the associated orderId(long) as String
		 */
		protected String handleSubmit() {
			try {
				// assign an id. 
				brokerAssignedId = Long.toString(orderId);
				orderHistory.setBrokerAssignedId(brokerAssignedId);
				// sending out the submit event to indicate that the order has
				// been submitted to the internal exchange ... 
				OrderEvent event = new OrderSubmitEvent(timeStampGenerator.generate(currentDate()),"ok");						
				super.fireOrderEvent(event);
				// ... and straight after that the order has been accepted ..
				event = new OrderAcceptEvent(timeStampGenerator.generate(currentDate()),"ok", Long.toString(orderId));
				super.fireOrderEvent(event);
				subscription = quoteSubscriptionSource.subscribe(localOrder.getInstrumentSpecification());
				assert(subscription!=null);
				subscription.addEventListener(new IEventListener<Quote>() {
					public void eventFired(Quote event) {
//						currentTimeStamp.set(event.getTimeStamp());
						checkOrder(event);
					}
				});
				subscription.activate();				
				return Long.toString(orderId);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		/**
		 * <strong>1.</strong> Checks whether the change of quantity(double) in the given newOrder(Order) won't make the associated openQuantity(double) a negative number since that would be illegal<br/>
		 * <strong>2.</strong> Sets the associated localOrder(Order) with a copy of the given newOrder(Order) and then creates and fires an OrderUpdateEvent whose timeStamp is from the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;) 
		 * , an "ok" message and the given newOrder(Order) as its updated(Order).
		 */
		public void handleUpdate(Order newOrder) {
			Order order = getOrder();
			double quantityDiff = newOrder.getQuantity() - order.getQuantity();
			if(openQuantity + quantityDiff < 0.) {
				throw new IllegalArgumentException("can not update to negative open quantity");
			}
			localOrder = new Order(newOrder);			
			OrderUpdateEvent event = new OrderUpdateEvent(timeStampGenerator.generate(currentDate()),"ok",newOrder);		
			super.fireOrderEvent(event);
		}
		/**
		 * <code>1.</code> Creates an OrderCancelEvent whose timeStamp is from the associated currentTimeStamp(AtomicReference&lt;TimeStamp&gt;) and an "at your request" message. Then it
		 * invokes the inherited <code>fireOrderEvent(orderEvent)</code> with it.<br/>
		 * <code>2.</code> Cancels the associated subscription(ISubscription&lt;Quote&gt;)
		 */
		public void handleCancel() {
			super.fireOrderEvent(new OrderCancelEvent(timeStampGenerator.generate(currentDate()),"at your request"));
			subscription.cancel();
		}
		
		private static final double EPSILON = 1e-6;
		/**
		 * <strong>1.</strong> The given quantity(double) is limited up to the associated openQuantity(double)<br/>
		 * <strong>2.</strong> If the given quantity(double) is less than EPSILON(1.0E-6), it cancels the associated subscription(ISubscription&lt;Quote&gt;) and returns without doing anything else<br/>
		 * <strong>3.</strong> Decreases the quantity(double) from the associated openQuantity(double)<br/>
		 * <strong>4.</strong> Creates and fires an OrderExecutionEvent using the given price(int), quantity(double) and commission = commissionPerExecution + quantity * commissionPerShare
		 * creates and fires an OrderExecutionEvent
		 * @param quantity
		 * @param price
		 */
		private void fireExecution(double quantity, double price) {
			if(quantity > openQuantity) {
				quantity = openQuantity;
			}
			
			if(quantity < EPSILON) {
				// order filled, unsubscribe
				subscription.cancel();
				return;
			}
	
			OrderExecutionEvent execution = new OrderExecutionEvent();
	
			execution.setEventTimeStamp(timeStampGenerator.generate(currentDate()));
			execution.setPrice(price);
			execution.setQuantity(quantity);
			double cumulatedQuantity=quantity;
			double averagePrice=price;
			if(executions.size()!=0){
				OrderExecutionEvent previousOrderExecutionEvent=executions.get(executions.size()-1);
				cumulatedQuantity+=previousOrderExecutionEvent.getCumulatedQuantity();
				averagePrice=((price*quantity)+(previousOrderExecutionEvent.getAveragePrice()*previousOrderExecutionEvent.getCumulatedQuantity()))/cumulatedQuantity;
			}
			execution.setCumulatedQuantity(cumulatedQuantity);
			execution.setAveragePrice(averagePrice);
	
			openQuantity -= quantity;
			fireOrderEvent(execution);
			double approximateCommission = CommissionsCalculator.determineApproximateCommissionForOrderPartialFill(localOrder,cumulatedQuantity, price); //commissions should be calculated for all executions so far not just this one
			OrderCommissionEvent orderCommissionEvent=new OrderCommissionEvent();
			orderCommissionEvent.setEventTimeStamp(timeStampGenerator.generate(currentDate()));
			orderCommissionEvent.setCommission(approximateCommission);
			Currency currency = spec.getCurrency();
			orderCommissionEvent.setCurrency(currency);
			fireOrderEvent(orderCommissionEvent);
		}
		/**
		 * Decides whether the associated localOrder(Order) can execute based on the given quote(Quote) and invokes <code>fireExecution(quantity, price)</code> if so.<br/>
		 * For a MARKET order, this is always true.<br/>
		 * For a LIMIT order the price must be within the limitPrice(double) of the associated localOrder(Order)<br/>
		 * For a STOP order the price must breach the stopPrice(double) of the associated localOrder(Order)<br/>
		 * For a TRAILING_STOP order, if first sets the stopPrice at the trailing distance. Then in each further call, if the price breaches the stopPrice(double) of the 
		 * associated localOrder(Order) it shoots a MARKET order, or it may readjust the stop price if necessary.
		 * @param quote
		 */
		private void checkOrder(Quote quote) {
			double quantity;
			double price;
			if(localOrder.getOrderSide() == OrderSide.BUY) {
				quantity = quote.getAskQuantity();
				price = quote.getAskPrice();
			} else {
				quantity = quote.getBidQuantity();
				price = quote.getBidPrice();				
			}
			// sanity check
			if(price == Quote.NOT_SET)
				return;
			if(quantity <= 0. || price <= 0.) {
				return;
			}
			if(quantity<100){
				quantity=100;
			}
			switch(localOrder.getOrderType()) {
			case MARKET:
				// fill it
				fireExecution(quantity, price);
				break;				
			case LIMIT:
				if(localOrder.getOrderSide() == OrderSide.BUY) {
					if(price <= localOrder.getLimitPrice()) {
						fireExecution(quantity, price);
					} 
				} else {
					if(price >= localOrder.getLimitPrice()) {
						fireExecution(quantity, price);
					} 
				}
				break;	
			case STOP:
				if(localOrder.getOrderSide() == OrderSide.BUY) {
					if(price >= localOrder.getStopPrice()) {
						fireExecution(quantity, price);
					} 
				} else {
					if(price <= localOrder.getStopPrice()) {
						fireExecution(quantity, price);
					} 
				}
				break;
			case STOP_LIMIT:
				if(localOrder.getOrderSide() == OrderSide.BUY) {
					if(price >= localOrder.getStopPrice()&&price <= localOrder.getLimitPrice()) {
						fireExecution(quantity, price);
					} 
				} else {
					if(price <= localOrder.getStopPrice()&&price >= localOrder.getLimitPrice()) {
						fireExecution(quantity, price);
					} 
				}
				break;	
			case TRAILING_STOP:
				if(localOrder.getStopPrice() <= 0.0) {
					// start trailing
					if(localOrder.getOrderSide() == OrderSide.BUY) {
						localOrder.setStopPrice(price + localOrder.getTrailingDistance());
					} else {
						localOrder.setStopPrice(price - localOrder.getTrailingDistance());
					}
				} else {
					if(localOrder.getOrderSide() == OrderSide.BUY) {
						if(price >= localOrder.getStopPrice()) {
//							log.info("trailing stop order with id: " + orderId + " hit STOP at " + price + ", converted to MARKET order");
							localOrder.setOrderType(OrderType.MARKET);
							fireExecution(quantity, price);
						} else if(price < localOrder.getStopPrice() - localOrder.getTrailingDistance()) {
							localOrder.setStopPrice(price + localOrder.getTrailingDistance());
//							log.info("trailing stop order with id: " + orderId + " adjusted to stop=" + localOrder.getStopPrice());
						} 
					} else {
						if(price <= localOrder.getStopPrice()) {
//							log.info("trailing stop order with id: " + orderId + " hit STOP at " + price + ", converted to MARKET order");
							localOrder.setOrderType(OrderType.MARKET);
							fireExecution(quantity, price);
						} else if(price > localOrder.getStopPrice() + localOrder.getTrailingDistance()) {
							localOrder.setStopPrice(price - localOrder.getTrailingDistance());
//							log.info("trailing stop order with id: " + orderId + " adjusted to stop=" + localOrder.getStopPrice());
						}
					}
				}
				break;
			case MARKET_TO_LIMIT://for now, just assume it works as a MARKET order for backesting
				// fill it
				fireExecution(quantity, price);
				break;
			default:
				throw new IllegalStateException("only MARKET/LIMIT/STOP/TRAILING_STOP orders are supported, received :" + localOrder.getOrderType());
			}
		}
		
		/**
		 * <strong>1.</strong> Updates/inserts the Position affected by the given event(OrderExecutionEvent) in the associated brokerAccount(BrokerAccount).
		 * For a SELL, SHORT_SELL or SHORT_SELL_EXEMPT Order the quantity(double) of the Position decreases, and for a BUY Order it increases.<br/>
		 * <strong>2.</strong> It also calculates the cashFlow as <code>event.getQuantity() * event.getPrice() - event.getCommission()</code> (SELL, SHORT_SELL or SHORT_SELL_EXEMPT Order) or 
		 * <code>- event.getQuantity() * event.getPrice() - event.getCommission()</code> (BUY Order) and then adds the cashFlow - event.getEventTimeStamp() pair into the
		 * balanceEntries(List&lt;BalanceEntry&gt;) of the balanceBook(BalanceBook) of the associated brokerAccount(BrokerAccount).<br/>
		 * <strong>3.</strong> If the Position's quantity is less than EPSILON(1e-6), it gets removed, otherwise it recalculates its average price as 
		 * position.getQuantity() * event.getPrice() / position.getQuantity()
		 * @param event
		 */
		private void processExecution(OrderExecutionEvent event) {
			Order order = new Order(localOrder);//tracker.getOrder();
			// get position for execution
			Position position; 
			Portfolio portfolio = brokerAccount.getPortfolio();
//			portfolioDao.update(portfolio);
			if (portfolio.hasPosition(order.getInstrumentSpecification())) {
				position = portfolio.getPosition(order.getInstrumentSpecification());
			} else {
				position = new Position();
				position.setInstrumentSpecification(order.getInstrumentSpecification());
				portfolio.addPosition(position);
			}
			// update the average price		
			double newValue;
			double cashFlow;			
			// mark-to-market 
			switch(order.getOrderSide()) {
			case SELL: case SHORT_SELL: case SHORT_SELL_EXEMPT:
				// decrease the amount of the position.
				position.setQuantity(position.getQuantity() - event.getQuantity());
				newValue = position.getQuantity() * event.getPrice();
				cashFlow = event.getQuantity() * event.getPrice() - event.getCommission();
				break;
			case BUY:
				// increase the position size.
				position.setQuantity(position.getQuantity() + event.getQuantity());
				newValue = position.getQuantity() * event.getPrice();
				cashFlow = - event.getQuantity() * event.getPrice() - event.getCommission();
				break;
			default:
				throw new IllegalArgumentException("order side not supported/implemented: " + order.getOrderSide());
			}	
			// clean out empty positions.
			if (Math.abs(position.getQuantity()) < EPSILON) {
				portfolio.removePosition(position);
				if(useDatabase){
					positionDao.delete(position);
				}
			} else {
				// calculate the new average price (from mark-to-market value).
				position.setAveragePrice(Math.abs(newValue / position.getQuantity()));
			}
			BalanceBook balanceBook=brokerAccount.getBalanceBook();
			balanceBook.addBalanceEntry(cashFlow, event.getEventTimeStamp());
			if(useDatabase){
				balanceBookDao.update(balanceBook);
				portfolio=portfolioDao.update(portfolio);
			}
		}
		
		private void processCommission(OrderCommissionEvent event) {
			if(event.isFinalCommissionEvent()){
				double cashFlow= - event.getCommission();
				BalanceBook balanceBook=brokerAccount.getBalanceBook();
				balanceBook.addBalanceEntry(cashFlow, event.getEventTimeStamp());
				if(useDatabase){
					balanceBookDao.update(balanceBook);
				}
			}
		}
	}
	/**
	 * if the OrderType of the given order(Order) is either MARKET, LIMIT, STOP or TRAILING_STOP, it creates and returns an OrderTracker(OrderTrackerBase sublass) 
	 * using the given order(Order) and its quantity(double) to set its inherited order(Order) and openQuantity(double).<br/>
	 * Sets its associated localOrder(Order) with a copy of the given order(Order).
	 */
	@Override
	protected OrderTracker createOrderTracker(Order order) {
		switch(order.getOrderType()) {
		case MARKET: case LIMIT: case STOP: case TRAILING_STOP: case MARKET_TO_LIMIT:
			break;
		default:
			throw new IllegalArgumentException("only MARKET/LIMIT/STOP/TRAILING_STOP orders are supported, received :" + order.getOrderType());
		}
		return new OrderTracker(order);
	}
}